package com.why.simpleboot.core.core.factory;

import com.why.simpleboot.core.annotation.applicationcontext.Value;
import com.why.simpleboot.core.annotation.ioc.Autowired;
import com.why.simpleboot.core.annotation.ioc.Component;
import com.why.simpleboot.core.annotation.ioc.Qualifier;
import com.why.simpleboot.core.annotation.mvc.RestController;
import com.why.simpleboot.core.common.utils.GeneralUtlis;
import com.why.simpleboot.core.common.utils.SimpleIocUtils;
import com.why.simpleboot.core.common.utils.SimpleReflectUtils;

import com.why.simpleboot.core.configration.ConfigurationFactory;
import com.why.simpleboot.core.configration.SimpleBootConfiguration;
import com.why.simpleboot.core.core.aop.interceptor.Interceptor;
import com.why.simpleboot.core.core.aop.processor.GenerateBeanPostProcessorFactory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @program: why-simpleboot-framework
 * @description:
 * @author: @why
 * @create: 2021-05-23 22:36
 **/
public class BeanFactory implements Factory {
    private static final Logger log = LoggerFactory.getLogger(BeanFactory.class);
    private final ConcurrentHashMap<String, Object> BEANS = new ConcurrentHashMap<>();
    private final ConcurrentHashMap<Class<?>, Object> TYPE_CACHE = new ConcurrentHashMap<>();
    private ConcurrentHashMap<String, Object> advanceAop = new ConcurrentHashMap<>();
    private final InterceptorFactory interceptorFactory;
    private DependencyInjection dependencyInjection;

    public BeanFactory(InterceptorFactory interceptorFactory) {
        this.interceptorFactory = interceptorFactory;
    }

    /**
     * 实例化组bean，直接将bean都实例化就ok了
     *
     * @param classes
     */
    public void loadBeans(ConcurrentHashMap<Class<? extends Annotation>,
            Set<Class<?>>> classes,
                          String... basePackages) {

        //获取所有bean，然后为其实例化加入map
        Set<Class<?>> components = classes.get(Component.class);
        Set<Class<?>> restControllers = classes.get(RestController.class);
        for (Class<?> bean : components) {
            Object instance = SimpleReflectUtils.getInstance(bean);
            String beanName = SimpleIocUtils.getBeanName(bean);
            if (instance != null) {
                BEANS.put(beanName, instance);
            }

        }
        for (Class<?> bean : restControllers) {
            Object instance = SimpleReflectUtils.getInstance(bean);
            if (instance != null) {
                BEANS.put(bean.getName(), instance);
            }
        }
        //完成对属性的依赖注入

        if (dependencyInjection == null) {
            dependencyInjection = new DependencyInjection(components);
        }

        log.info("dependencyInjection start~");
        for (Map.Entry<String, Object> entry : BEANS.entrySet()) {

            dependencyInjection.populateBean(entry.getValue());
        }
//        应用后置处理器  也就完成AOP操作
        applyBeanPostProcessors();
        advanceAop = null;
    }

    /**
     * 应用所有的后置处理器
     */
    public void applyBeanPostProcessors() {

        BEANS.replaceAll((beanName, beanInstance) -> {
            if (advanceAop.containsKey(beanName)) {
                return advanceAop.get(beanName);
            }
            Object wrapperBean = beanInstance;
            List<Interceptor> interceptors = interceptorFactory.getInterceptors();
            wrapperBean = GenerateBeanPostProcessorFactory.getProxyBean(wrapperBean, interceptors);
            return wrapperBean;
        });

    }

    /**
     * @param beanName 无法通过名字找到该对象
     * @return
     */
    public Object getBeanByName(String beanName) {
        if (!BEANS.containsKey(beanName)) {
            log.warn("The object cannot be found by name!");
        }
        return BEANS.get(beanName);
    }

    /**
     * 通过类型找到Bean
     *
     * @param type
     * @return
     */
    public Object getBeanByType(Class<?> type) {
        if (TYPE_CACHE.containsKey(type)) {
            return TYPE_CACHE.get(type);
        }
//        先尝试通过名字获取
        String beanName = SimpleIocUtils.getBeanName(type);
        Object result = getBeanByName(beanName);

        if (result == null) {
            for (Map.Entry<String, Object> entry : BEANS.entrySet()) {
                Object instance = entry.getValue();
                /**
                 * 有两个Class类型的类象，一个是调用isAssignableFrom方法的类对象（后称对象a），以及方法中作为参数的这个类对象（称之为对象b），这两个对象如果满足以下条件则返回true，否则返回false：
                 *
                 *     a对象所对应类信息是b对象所对应的类信息的父类或者是父接口，简单理解即a是b的父类或接口
                 *
                 *     a对象所对应类信息与b对象所对应的类信息相同，简单理解即a和b为同一个类或同一个接口
                 */
//                如果容器中的实例是type的父类or同一级类，那就返回
                if (instance.getClass().isAssignableFrom(type)) {
                    result = instance;
                    break;
                }
            }
        }
        if (result == null) {
            // TODO: 2021/5/28
            log.warn("The object cannot be found by type!");
            return null;
        }

        TYPE_CACHE.put(type, result);
        return result;
    }

    public void setBean(String beanName, Object obj) {
        this.BEANS.put(beanName, obj);
    }

    public ConcurrentHashMap<String, Object> getBEANS() {
        return BEANS;
    }

    private class DependencyInjection {
        private Set<Class<?>> components;

        public DependencyInjection(Set<Class<?>> components) {
            this.components = components;
        }


        /**
         * 完成对bean的依赖注入
         *
         * @param incompleteBean
         */
        @SuppressWarnings("unchecked")
        public void populateBean(Object incompleteBean) {
            //@value属性的注入
            SimpleBootConfiguration config = ConfigurationFactory.getConfig();

            Field[] beanFields = incompleteBean.getClass().getDeclaredFields();
            if (beanFields.length > 0) {
                for (Field field : beanFields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Object filedInstance = getFieldInstance(field);
                        SimpleReflectUtils.setFieldForTargetObject(incompleteBean, field, filedInstance);

                    }
                    if (field.isAnnotationPresent(Value.class)) {
                        Value v = field.getAnnotation(Value.class);
                        String key = v.value();
                        String value = config.getString(key);
                        if (value == null) {
                            throw new IllegalArgumentException("can not find target value for property:{" + key + "}");
                        }
                        Object convertedValue = GeneralUtlis.convert(field.getType(), value);
                        SimpleReflectUtils.setFieldForTargetObject(incompleteBean, field, convertedValue);


                    }

                }
            }
        }

        /**
         * 得到bean的属性实例对象
         *
         * @param beanField
         * @return
         */
        private Object getFieldInstance(Field beanField) {
            Class<?> beanFieldClass = beanField.getType();
//            bean的属性名字并获取其在容器中的属性
            String beanFieldName = SimpleIocUtils.getBeanName(beanFieldClass);
//            Object beanFieldInstance = BEANS.get(beanFieldName);
//            先根据类的名字获取  如果没有那就遍历集合 尝试按照类型获取
            Object beanFieldInstance = getBeanByType(beanFieldClass);

//            没有在容器中找到bean，尝试去扫描一下包
            if (beanFieldInstance == null) {
                //            假如是接口会尝试为其找到实现类
                if (beanFieldClass.isInterface()) {
//                    是接口，并且被注解标注了 获取
                    if (beanField.isAnnotationPresent(Qualifier.class)) {
                        Qualifier qualifier = beanField.getDeclaredAnnotation(Qualifier.class);
                        beanFieldName = qualifier == null ? beanFieldName : qualifier.value();
                        beanFieldInstance = BEANS.get(beanFieldName);
                    } else {
//                        是接口，并且没有使用Qualifier标注实现类，那就尝试去容器中获取一下接口对应的实现类 如果发现多个，抛出异常
                        int i = 0;
                        for (Class<?> component : components) {
                            if (SimpleReflectUtils.isSubClass(beanFieldClass, component)) {
                                i++;
                                String name = SimpleIocUtils.getBeanName(component);
                                beanFieldInstance = BEANS.get(name);
                            }
                            if (i > 1) {
                                throw new IllegalArgumentException("Too many implementation" +
                                        " classes found,please for field{" + beanField + "} Qualifier a class");
                            }
                        }
                    }
                }
            }

            if (beanFieldInstance == null) {
                throw new NullPointerException("dependencyInjection defeat,No objects of this type were found:" + beanFieldClass.getName());
            }
            //            看看是否需要提前完成aop
            beanFieldInstance = wrapperBeanIfNecessary(beanFieldInstance);

            return beanFieldInstance;
        }

        /**
         * 尝试提前生成代理对象 保证依赖注入一致性
         *
         * @param beanFieldInstance
         * @return
         */
        private Object wrapperBeanIfNecessary(Object beanFieldInstance) {
            Class<?> clazz = beanFieldInstance.getClass();
            String beanName = SimpleIocUtils.getBeanName(clazz);

            if (advanceAop.containsKey(beanName)) {
                return advanceAop.get(beanName);
            }
//            开始提前生成代理类
            Object wrapperBean = beanFieldInstance;
            List<Interceptor> interceptors = interceptorFactory.getInterceptors();
//            对目标类生成代理
            wrapperBean = GenerateBeanPostProcessorFactory.getProxyBean(wrapperBean, interceptors);
            advanceAop.put(beanName, wrapperBean);
            return wrapperBean;


        }


    }


}


